# -*- coding: utf-8 -*-
import urllib
import sys
import traceback
import re
import os
import cStringIO as StringIO
import time
import datetime
import workerpool
import mechanize
import logging
import commonlib
import urllib2
from urllib import urlretrieve
from lxml import etree
from pymongo import Connection
from urlparse import urljoin
from mongolog.handlers import MongoHandler

localFilepath = '/home/hoangnamhai/HarvestedData/tintuc/xalo/'
prefix = 'tintuc/xalo'
mongoServer = 'beta.mana.vn'    # Server Mana 64bit
mongoPort = 27017
saveImageServiceUri = 'http://beta.mana.vn:3334/downloadImage'

def getUrlMainPart(url):
    ''' Do url của Xa lộ có include 1 số ký tự random, các ký tự này sẽ biến đổi theo ngày.
        Vì vậy, để tránh bị crawl lại dữ liệu cũ, cần xác định phần url cố định của Xa lộ.
        Vd: input
            http://m.xalo.vn/17___5_xahoib_1_______.mtt_5_xahoi1_1__ffffcb632a528020_9cb3ed7____.mad
            http://m.xalo.vn/1_o_7_suckhoe1_1__12cbe463160_8932e5b____.mad '''
    result = commonlib.extractWithRegEx(r"__([0-9,a-f]+_[0-9,a-f]+)__", url, 1).strip()
    if result == '': result = url
    print 'getUrlMainPart ', result
    return result

def saveImage(url):
    try:
        params = {'url':url, 'prefix':prefix}
        req = urllib2.Request(saveImageServiceUri, urllib.urlencode(params))
        data = urllib2.urlopen(req).read()
        print data
    except:
        print traceback.print_exc()

def strToASCII(str):
    if str == '': return ''
    listPattern = ["á|à|ả|ạ|ã|â|ấ|ầ|ẩ|ậ|ẫ|ă|ắ|ằ|ẳ|ặ|ẵ", "Á|À|Ả|Ạ|Ã|Â|Ấ|Ầ|Ẩ|Ậ|Ẫ|Ă|Ắ|Ằ|Ẳ|Ặ|Ẵ",
                   "đ", "Đ", "í|ì|ỉ|ị|ĩ", "Í|Ì|Ỉ|Ị|Ĩ", "é|è|ẻ|ẹ|ẽ|ê|ế|ề|ể|ệ|ễ", "É|È|Ẻ|Ẹ|Ẽ|Ê|Ế|Ề|Ể|Ệ|Ễ",
                   "ó|ò|ỏ|ọ|õ|ô|ố|ồ|ổ|ộ|ỗ|ơ|ớ|ờ|ở|ợ|ỡ", "Ó|Ò|Ỏ|Ọ|Õ|Ô|Ố|Ồ|Ổ|Ộ|Ỗ|Ơ|Ớ|Ờ|Ở|Ợ|Ỡ",
                   "ú|ù|ủ|ụ|ũ|ư|ứ|ừ|ử|ự|ữ", "Ú|Ù|Ủ|Ụ|Ũ|Ư|Ứ|Ừ|Ử|Ự|Ữ", "ý|ỳ|ỷ|ỵ|ỹ", "Ý|Ỳ|Ỷ|Ỵ|Ỹ"]
    rep = ['a', 'A', 'd', 'D', 'i', 'I', 'e', 'E', 'o', 'O', 'u', 'U', 'y', 'Y']
    str = str.encode('utf-8', 'replace')
    for idx in range(len(listPattern)):
        str = re.sub(listPattern[idx], rep[idx], str)
    return str

def buildTree(url, outHTML=False):
    try:
        opener = mechanize.build_opener(mechanize.HTTPRefererProcessor, mechanize.HTTPErrorProcessor)
        opener.addheaders = [("User-Agent", "Nokia2730c-1b/2.0 (07.70) Profile/MIDP-2.1 Configuration/CLDC-1.1")]
        response = opener.open(url)
        html = response.read()
        opener.close()
        if outHTML: print html
        parser = etree.HTMLParser(encoding='utf-8')
        return etree.parse(StringIO.StringIO(html), parser)
    except:
        return ''
        raise

def standardlizeTimeValue(timeStr):
    ''' Dùng để tính lại thời gian với các giá trị kiểu như: 34 phút trước, 1 giờ trước, hôm qua, hôm kia, 08/11/2010 '''
    now = datetime.datetime.now()
    result = now
    for patName in listPat.keys():
        m = listPat[patName].search(timeStr)
        if m is not None:
            if patName == 'phut':
                result = now - datetime.timedelta(minutes = int(m.group(1)))
            elif patName == 'gio':
                result = now - datetime.timedelta(hours = int(m.group(1)))
            elif patName == 'homqua':
                result = now - datetime.timedelta(days = 1)
            elif patName == 'homkia':
                result = now - datetime.timedelta(days = 2)
            elif patName == 'ngay':
                result = datetime.datetime(day = int(m.group(1)), month = int(m.group(2)), year = int(m.group(3)),
                                           hour = now.hour, minute = now.minute, second = now.second)
    return result

def processArticle(url):
    log.debug('processArticle({0})'.format(url))
    totalLen = 0
    try:
        results = []
        while url != '':
            print "processArticle {0}".format(url)
            tree = buildTree(url)
            for item in tree.xpath("//div[@id='top'][@class='p']/descendant-or-self::*"):
#                print item.tag
                if item.tag == 'a':
                    hrefLink = urllib.unquote(commonlib.extractWithRegEx(r"url=(.+)&type=ni", urljoin(url, item.get('href')), 1)).strip()
                    if hrefLink != '':
                        results.append({'type':'img', 'data': hrefLink})
                        saveImage(hrefLink)
                        totalLen += len(hrefLink)
                if item.text is not None and item.tag != 'a':
                    itemText = item.text.strip()
                    if itemText != '':
                        totalLen += len(itemText)
#                        print itemText
                        if item.tag == 'b':
                            results.append({'type':'textbold', 'data': itemText})
                        else:
                            results.append({'type':'text', 'data': itemText})
                if item.tail is not None:
                    itemText = item.tail.strip()
                    if itemText != '':
#                        print itemText
                        totalLen += len(itemText)
                        if item.tag == 'b':
                            results.append({'type':'textbold', 'data': itemText})
                        else:
                            results.append({'type':'text', 'data': itemText})
                            
            tiepElem = tree.xpath(unicode("//div[@class='r']//a[contains(.,'Ti')]"))
            if len(tiepElem)>0:
                url = urljoin(url, tiepElem[0].get('href'))
            else:
                url = ''
        
        # Những bài viết có quá ít kí tự thì coi là lỗi, không ghi lại
        print totalLen 
        if totalLen < 150:
            print 'WARNING - Có {0} kí tự trong bài viết - {1}'.format(totalLen, url)
            return []
        else:   return results
        
    except:
        print 'ERROR ----- processArticle {0}'.format(url)
        traceback.print_exc()
        return []

def processPageList(url, chuyenMucId, currentProcessed=0, currentDup=0):
    print 'processPageList: ' + url
    log.debug('processPageList: {0}, {1}'.format(chuyenMucId, url))
    tree = buildTree(url)
    connection = Connection(mongoServer, mongoPort)
    db = connection['tintuc']
    collection = db[chuyenMucId]
    global totalNewsCrawled
    for artElem in tree.xpath("//div[@class='i']"):
        try:
            result = {}
            result['link'] = str(artElem.xpath('.//a')[0].get('href'))
            result['link'] = urljoin(url, result['link'])
            
            # 12/7/2010: Do xa lộ có link bài viết biến động theo ngày nên phải thêm hàm getUrlMainPart
            # để xác định phần cố định của URL này rồi mới chuyển vào để làm ID
            result['_id'] = commonlib.getMD5Hash(getUrlMainPart(result['link']))
            isExist = collection.find_one({'_id': result['_id']})
            if not isExist:
                currentProcessed += 1 
                result['tieude'] = artElem.xpath('.//a')[0].text.strip()
                # print result['tieude'].decode('utf-8')
                temp = artElem.xpath('.//span[1]')
                result['nguontin'] = artElem.xpath('.//span[1]')[0].text.strip()
                result['thoigian'] = artElem.xpath('.//span[2]')[0].text.strip()
                result['thoigian'] = standardlizeTimeValue(result['thoigian'])
                
                temp = artElem.xpath('.//img')
                if len(temp) > 0:
                    result['thumb'] = temp[0].get('src').strip()
                    result['thumb'] = urljoin(url, result['thumb'])
                    saveImage(result['thumb'])
                # print result
                result['detail'] = processArticle(result['link'])
                if len(result) < 1: continue
                result['lastupdate'] = str(datetime.datetime.now())
                result['timestamp'] = time.time()
                
                # Đề phòng trường hợp một số link không có dữ liệu
                if len(result['link']) > 3:
                    totalNewsCrawled += 1
                    collection.save(result)
            else:
                currentDup = currentDup + 1
                print 'Article existed: {0} -> {1}'.format(result['link'], result['_id']) 
        except:
            print 'processPageList exception: {0}'.format(url)
            traceback.print_exc()
            raise
    print '{0}: Current procesed = {1}; Current dup = {2}'.format(chuyenMucId, currentProcessed, currentDup)
    connection.disconnect()
    return currentProcessed, currentDup

def processPageList2(url, chuyenMucId, currentProcessed=0, currentDup=0):
    ''' Các anh Xa lộ củ chuối, từ trang 6 trở đi các anh ý format kiểu khác. Vậy nên sinh ra hàm này để
        xử lý các trang với p>=6.
        Chú ý: 
            - trong page listing dạng này các bài viết không có thông tin về nguồn tin.
            - không có thông tin về ảnh thumbnail
    '''
    print 'processPageList2: ' + url
    log.debug('processPageList2: {0}, {1}'.format(chuyenMucId, url))
    tree = buildTree(url)
    connection = Connection('localhost', 27017)
    db = connection['tintuc']
    collection = db[chuyenMucId]
    global totalNewsCrawled
    for artElem in tree.xpath("//div[@class='w']"):
        try:
            result = {}
            result['link'] = str(artElem.xpath('.//a')[0].get('href'))
            result['link'] = urljoin(url, result['link'])
            result['_id'] = commonlib.getMD5Hash(getUrlMainPart(result['link']))
            
            isExist = collection.find_one({'_id': result['_id']})
            if not isExist:
                currentProcessed += + 1 
                result['tieude'] = artElem.xpath('.//a')[0].text.strip()
                # print result['tieude']
                print result['link']
                temp = artElem.xpath('.//span[1]')
                # result['nguontin'] = artElem.xpath('.//span[1]')[0].text.strip()
                result['thoigian'] = artElem.xpath('.//span[1]')[0].text.strip()
                result['thoigian'] = standardlizeTimeValue(result['thoigian'])
                
                temp = artElem.xpath('.//img')
                if len(temp) > 0:
                    result['thumb'] = temp[0].get('src').strip()
                    result['thumb'] = urljoin(url, result['thumb'])
                    # saveImage(result['thumb'])
                # print result
                result['detail'] = processArticle(result['link'])
                if len(result) < 1: continue
                result['lastupdate'] = str(datetime.datetime.now())
                result['timestamp'] = time.time()
                totalNewsCrawled += 1
                collection.save(result)
            else:
                currentDup = currentDup + 1
                print 'Article existed: {0} -> {1}'.format(result['link'], result['_id']) 
        except:
            print 'processPageList2 exception: {0}'.format(url)
            log.error('processPageList2 exception: {0}'.format(url))
            exc_type, exc_value, exc_traceback = sys.exc_info()
            print repr(traceback.format_exception(exc_type, exc_value, exc_traceback))
            log.error(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)))
            raise
    print '{0}: Current procesed = {1}; Current dup = {2}'.format(chuyenMucId, currentProcessed, currentDup)
    connection.disconnect()
    return currentProcessed, currentDup

def processChuyenMuc(chuyenMucId):
    duplicateThreshold = 30
    maxProcessThreshold = 200
    currentDupThresHold = 0
    currentProcessThreshold = 0
    for pageIndex in range(1, maxPage):
        if currentDupThresHold >= duplicateThreshold:
            print 'Chuyên mục: {0} dừng vì trùng quá ngưỡng cho phép'.format(chuyenMucId)
            return
        if currentProcessThreshold >= maxProcessThreshold:
            print 'Chuyên mục: {0} dừng vì đã xử lý nhiều quá ngưỡng cho phép'.format(chuyenMucId)
            return
        url = 'http://m.xalo.vn/{0}.mcm?p={1}&t={2}'.format(chuyenMucId, pageIndex, time.time())
        if pageIndex <= 5:
            currentProcessThreshold, currentDupThresHold = processPageList(url, chuyenMucId, currentProcessThreshold, currentDupThresHold)
        else:        
            currentProcessThreshold, currentDupThresHold = processPageList2(url, chuyenMucId, currentProcessThreshold, currentDupThresHold)
    return


if __name__ == '__main__':
    listChuyenMuc = ['xahoi','thegioi','thethao','kinhdoanh','giaitri','giaoduc','phapluat','suckhoe','tinhyeugioitinh','khoahoc','congngheso','otoxemay','chuyenla']
    listPat = {'phut': re.compile(r"(\d+)\sphút trước"), 'gio': re.compile(r"(\d+)\sgiờ trước"), 
           'homqua': re.compile(r"hôm qua"), 'homkia': re.compile(r"hôm kia"),
           'ngay': re.compile(r"(\d+)/(\d+)/(\d+)")}
    maxPage = 40
    totalNewsCrawled = 0
    
    log = logging.getLogger('xaloNews')
    log.setLevel(logging.INFO)
    log.addHandler(MongoHandler.to('mongolog', 'log'))
    
    log.debug('Start crawler tin tuc xalo.vn')
    pool = workerpool.WorkerPool(size=10)
    pool.map(processChuyenMuc, listChuyenMuc)
    pool.shutdown()
    pool.wait()
    
    log.debug('Finished crawler tin tuc xalo.vn')
    log.info('Total news crawled xalo.vn: {0}'.format(totalNewsCrawled))
    print "Đã xong", datetime.datetime.now()
    os._exit(0)